package com.zatech.cgnci.project.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zatech.cgnci.project.base.enums.ClaimReinsurerTypeEnum;
import com.zatech.cgnci.project.base.enums.ResponseCodeEnum;
import com.zatech.cgnci.project.base.exception.GlobalException;
import com.zatech.cgnci.project.base.utils.ConvertUtils;
import com.zatech.cgnci.project.base.utils.VerifyUtils;
import com.zatech.cgnci.project.dto.CgnciClaimSettlementDTO;
import com.zatech.cgnci.project.dto.CgnciClaimSettlementQueryDTO;
import com.zatech.cgnci.project.dto.FlowTaskApprovalDTO;
import com.zatech.cgnci.project.dto.FlowTaskBackDTO;
import com.zatech.cgnci.project.dto.FlowTaskSubmitDTO;
import com.zatech.cgnci.project.dto.SettlementQueryDTO;
import com.zatech.cgnci.project.entity.CgnciApprovalInfoDO;
import com.zatech.cgnci.project.entity.CgnciApprovalRecordDO;
import com.zatech.cgnci.project.entity.CgnciClaimSettlementDO;
import com.zatech.cgnci.project.entity.CgnciClaimSettlementDetailDO;
import com.zatech.cgnci.project.entity.CgnciClaimSettlementReinsurerDO;
import com.zatech.cgnci.project.mapper.CgnciApprovalInfoMapper;
import com.zatech.cgnci.project.mapper.CgnciApprovalRecordMapper;
import com.zatech.cgnci.project.mapper.CgnciClaimSettlementDetailMapper;
import com.zatech.cgnci.project.mapper.CgnciClaimSettlementMapper;
import com.zatech.cgnci.project.mapper.CgnciClaimSettlementReinsurerMapper;
import com.zatech.cgnci.project.service.CgnciClaimSettlementService;
import com.zatech.cgnci.project.service.IApprovalService;
import com.zatech.cgnci.project.vo.ApprovalRecordVO;
import com.zatech.cgnci.project.vo.CgnciClaimSettlementPageVO;
import com.zatech.cgnci.project.vo.CgnciClaimSettlementVO;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;


@Service
public class CgnciClaimSettlementServiceImpl extends ServiceImpl<CgnciClaimSettlementMapper, CgnciClaimSettlementDO> implements CgnciClaimSettlementService {
    @Autowired
    private CgnciClaimSettlementMapper claimSettlementMapper;
    @Autowired
    private CgnciClaimSettlementReinsurerMapper reinsurerMapper;
    @Autowired
    private CgnciClaimSettlementDetailMapper detailMapper;
    @Autowired
    private CgnciApprovalInfoMapper approvalInfoMapper;
    @Autowired
    private CgnciApprovalRecordMapper approvalRecordMapper;
    @Autowired
    private IApprovalService approvalService;

    @Override
    public IPage<CgnciClaimSettlementPageVO> pageClaimSettlement(CgnciClaimSettlementQueryDTO queryDTO) {
        return this.baseMapper.pageClaimSettlement(queryDTO.toPage(), queryDTO);
    }

    @Override
    public CgnciClaimSettlementVO detail(Long settlementId) {
        // 查询主表
        CgnciClaimSettlementDO settlementDO = this.getById(settlementId);
        if (VerifyUtils.isEmpty(settlementDO)) {
            throw new GlobalException(ResponseCodeEnum.PARAMS_ILLEGAL, "settlementId有误");
        }
        CgnciClaimSettlementVO vo = ConvertUtils.convert(settlementDO, CgnciClaimSettlementVO.class);

        // 查询再保人
        List<CgnciClaimSettlementReinsurerDO> reinsurerDOs = reinsurerMapper.selectList(
                new LambdaQueryWrapper<CgnciClaimSettlementReinsurerDO>()
                        .eq(CgnciClaimSettlementReinsurerDO::getSettlementId, settlementId)
        );
        List<CgnciClaimSettlementVO.ReinsurerVO> reinsurerVOs = ConvertUtils.convert(reinsurerDOs, CgnciClaimSettlementVO.ReinsurerVO.class);

        // 查询明细
        for (CgnciClaimSettlementVO.ReinsurerVO reinsurerVO : reinsurerVOs) {
            List<CgnciClaimSettlementDetailDO> detailDOs = detailMapper.selectList(
                    new LambdaQueryWrapper<CgnciClaimSettlementDetailDO>()
                            .eq(CgnciClaimSettlementDetailDO::getReinsurerId, reinsurerVO.getReinsurerId())
            );
            reinsurerVO.setDetails(ConvertUtils.convert(detailDOs, CgnciClaimSettlementVO.DetailVO.class));
        }

        // 查询汇总信息
        List<CgnciClaimSettlementVO.SettlementSummaryVO> summaries =
                this.baseMapper.getSettlementSummary(settlementId);
        vo.setSummaries(summaries);

        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(CgnciClaimSettlementDTO settlementDTO) {
        // 校验则
//        validateSettlement(settlementDTO);

        // 保存主表
        CgnciClaimSettlementDO settlementDO = ConvertUtils.convert(settlementDTO, CgnciClaimSettlementDO.class);
        settlementDO.setSettlementType("PREPAYMENT");
        this.save(settlementDO);

        // 保存再保人和明细
//        saveReinsurerAndDetail(settlementDO.getSettlementId(), settlementDTO.getReinsurers());
    }

    /**
     * 校验结算信息
     */
/*    private void validateSettlement(CgnciClaimSettlementDTO settlementDTO) {
        // 1. 校验再保人信息
        for (CgnciClaimSettlementDTO.ReinsurerDTO reinsurerDTO : settlementDTO.getReinsurers()) {
            // 按险种-标的-金额类型分组统计转分摊回金额
            Map<String, BigDecimal> recoveryAmountMap = reinsurerDTO.getDetails().stream()
                    .filter(detail -> "RECOVERY".equals(detail.getDetailType()))
                    .collect(Collectors.groupingBy(
                            detail -> detail.getRiskType() + "-" + detail.getSubjectName() + "-" + detail.getAmountType(),
                            Collectors.reducing(BigDecimal.ZERO, CgnciClaimSettlementDTO.DetailDTO::getSettlementAmount, BigDecimal::add)
                    ));

            // 校验每个险种-标��-金额类型的转分摊回金额不得大于分入金
            for (CgnciClaimSettlementDTO.DetailDTO detail : reinsurerDTO.getDetails()) {
                if ("INWARD".equals(detail.getDetailType())) {
                    String key = detail.getRiskType() + "-" + detail.getSubjectName() + "-" + detail.getAmountType();
                    BigDecimal recoveryAmount = recoveryAmountMap.getOrDefault(key, BigDecimal.ZERO);
                    if (recoveryAmount.compareTo(detail.getSettlementAmount()) > 0) {
                        throw new GlobalException(ResponseCodeEnum.PARAMS_ILLEGAL,
                                String.format("标的%s-险种%s的转分摊回金额大于分入计算金额，请修改",
                                        detail.getSubjectName(), detail.getRiskType()));
                    }
                }
            }
        }
    }*/

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void finalSettlement(CgnciClaimSettlementDTO settlementDTO) {
        // 保存主表
        CgnciClaimSettlementDO settlementDO = ConvertUtils.convert(settlementDTO, CgnciClaimSettlementDO.class);
        settlementDO.setSettlementType("FINAL");
        this.save(settlementDO);

        // 保存再保人和明细
//        saveReinsurerAndDetail(settlementDO.getSettlementId(), settlementDTO.getReinsurers());
    }

/*    private void saveReinsurerAndDetail(Long settlementId, List<CgnciClaimSettlementDTO.ReinsurerDTO> reinsurers) {
        for (CgnciClaimSettlementDTO.ReinsurerDTO reinsurerDTO : reinsurers) {
            // 保存再保人
            CgnciClaimSettlementReinsurerDO reinsurerDO = ConvertUtils.convert(reinsurerDTO, CgnciClaimSettlementReinsurerDO.class);
            reinsurerDO.setSettlementId(settlementId);
            reinsurerMapper.insert(reinsurerDO);

            // 保存明细
            for (CgnciClaimSettlementDTO.DetailDTO detailDTO : reinsurerDTO.getDetails()) {
                CgnciClaimSettlementDetailDO detailDO = ConvertUtils.convert(detailDTO, CgnciClaimSettlementDetailDO.class);
                detailDO.setReinsurerId(reinsurerDO.getReinsurerId());
                // 计算原始币种金额
//                detailDO.setSettlementAmountOriginal(detailDTO.getSettlementAmount().multiply(new BigDecimal(String.valueOf(reinsurerDTO.getOriginalCurrency()))));
                detailMapper.insert(detailDO);
            }
        }
    }*/

    @Override
    public void submit(FlowTaskSubmitDTO flowTaskDTO) {
        approvalService.submit(flowTaskDTO);
    }

    @Override
    public void approve(FlowTaskApprovalDTO flowTaskDTO) {
        // 1. 调用审批服务
        approvalService.approve(flowTaskDTO);

        // 2. 处理重开审批
//        CgnciApprovalInfoDO approvalInfo = approvalInfoMapper.selectById(flowTaskDTO.getApprovalInfoId());
//        if ("011".equals(approvalInfo.getApprovalConfigGroupId())) { // 重开案件审批
//            CgnciClaimSettlementDO updateDO = new CgnciClaimSettlementDO();
//            updateDO.setSettlementId(Long.valueOf(approvalInfo.getFormId()));
//            if ("PASS".equals(flowTaskDTO.getApprovalResult())) {
//                updateDO.setTaskStatus("REOPENED"); // 已重开
//            } else {
//                updateDO.setTaskStatus("CLOSED"); // 驳回后恢复已结案状态
//            }
//            updateDO.setLatestAuditOpinion(flowTaskDTO.getApprovalOpinion());
//            this.updateById(updateDO);
//        }
    }

    @Override
    public void back(FlowTaskBackDTO flowTaskDTO) {
        approvalService.back(flowTaskDTO);
    }

    @Override
    public void cancel(String taskId) {
        CgnciApprovalInfoDO approvalInfo = approvalInfoMapper.selectById(taskId);
        if (VerifyUtils.isEmpty(approvalInfo)) {
            throw new GlobalException(ResponseCodeEnum.PARAMS_ILLEGAL, "taskId有误");
        }
        // 更新任务状态为已注销
//        approvalInfo("CANCEL");
        approvalInfoMapper.updateById(approvalInfo);
    }

    @Override
    public List<ApprovalRecordVO> log(Long settlementId) {
        List<CgnciApprovalRecordDO> approvalRecordDOList = approvalRecordMapper.selectList(
                new LambdaQueryWrapper<CgnciApprovalRecordDO>()
                        .eq(CgnciApprovalRecordDO::getFormId, settlementId)
        );
        return ConvertUtils.convert(approvalRecordDOList, ApprovalRecordVO.class);
    }

    @Override
    public List<CgnciClaimSettlementVO> queryClaimSettlementByCaseNo(String caseNo) {
        List<CgnciClaimSettlementVO> list = new ArrayList<>();
        LambdaQueryWrapper<CgnciClaimSettlementDO> querySettlement = new LambdaQueryWrapper<CgnciClaimSettlementDO>()
                .eq(CgnciClaimSettlementDO::getCaseNo, caseNo);
        List<CgnciClaimSettlementDO> settlementList = claimSettlementMapper.selectList(querySettlement);
        if (CollectionUtils.isEmpty(settlementList)) {
            return list;
        }
        for (CgnciClaimSettlementDO claimSettlementDO : settlementList) {
            CgnciClaimSettlementVO item = getClaimSettlementVO(claimSettlementDO);
            if (Objects.nonNull(item)) list.add(item);
        }
        return list;
    }

    @Override
    public CgnciClaimSettlementVO settlementQuery(SettlementQueryDTO req) {
        LambdaQueryWrapper<CgnciClaimSettlementDO> querySettlement = new LambdaQueryWrapper<CgnciClaimSettlementDO>()
                .eq(CgnciClaimSettlementDO::getCaseNo, req.getCaseNo())
                .eq(CgnciClaimSettlementDO::getSettlementNo, req.getSettlementNo())
                .eq(CgnciClaimSettlementDO::getSettlementType, req.getSettlementType());
        List<CgnciClaimSettlementDO> settlementList = claimSettlementMapper.selectList(querySettlement);
        if (CollectionUtils.isEmpty(settlementList)) {
            return null;
        }
        return getClaimSettlementVO(settlementList.get(0));
    }

    private CgnciClaimSettlementVO getClaimSettlementVO(CgnciClaimSettlementDO params) {
        CgnciClaimSettlementVO claimSettlementVO = new CgnciClaimSettlementVO();
        BeanUtils.copyProperties(params, claimSettlementVO);
        if (StringUtils.isNotBlank(params.getExtra())) {
            claimSettlementVO.setExtra(JSONObject.parseObject(params.getExtra()));
        }
        List<CgnciClaimSettlementVO.ReinsurerVO> intoSettlementReinsurerList = new ArrayList<>();
        List<CgnciClaimSettlementVO.ReinsurerVO> outSettlementReinsurerList = new ArrayList<>();
        List<CgnciClaimSettlementReinsurerDO> reinsurerList = reinsurerMapper.selectList(
                new LambdaQueryWrapper<CgnciClaimSettlementReinsurerDO>()
                        .eq(CgnciClaimSettlementReinsurerDO::getSettlementId, params.getSettlementId()));
        if (CollectionUtils.isEmpty(reinsurerList)) return null;
        List<CgnciClaimSettlementVO.ReinsurerVO> reinsurerVOList = ConvertUtils.convert(reinsurerList, CgnciClaimSettlementVO.ReinsurerVO.class);
        for (CgnciClaimSettlementVO.ReinsurerVO reinsurerVO : reinsurerVOList) {
            if (ClaimReinsurerTypeEnum.CEDING.getCode().equals(reinsurerVO.getReinsurerType())) {
                reinsurerVO.setDetails(getDetailByReinsurerId(reinsurerVO.getReinsurerId()));
                buildTotalSettlementAmount(reinsurerVO);
                intoSettlementReinsurerList.add(reinsurerVO);
            } else {
                reinsurerVO.setDetails(getDetailByReinsurerId(reinsurerVO.getReinsurerId()));
                buildTotalSettlementAmount(reinsurerVO);
                outSettlementReinsurerList.add(reinsurerVO);
            }
        }
        claimSettlementVO.setIntoSettlementList(intoSettlementReinsurerList);
        claimSettlementVO.setSpreadBackSettlementList(outSettlementReinsurerList);
        return claimSettlementVO;
    }

    private List<CgnciClaimSettlementVO.DetailVO> getDetailByReinsurerId(Long reinsurerId) {
        List<CgnciClaimSettlementDetailDO> detailDOs = detailMapper.selectList(
                new LambdaQueryWrapper<CgnciClaimSettlementDetailDO>()
                        .eq(CgnciClaimSettlementDetailDO::getReinsurerId, reinsurerId)
        );
        return ConvertUtils.convert(detailDOs, CgnciClaimSettlementVO.DetailVO.class);
    }

    private void buildTotalSettlementAmount(CgnciClaimSettlementVO.ReinsurerVO reinsurer) {
        if (Objects.nonNull(reinsurer) && CollectionUtils.isNotEmpty(reinsurer.getDetails())) {
            BigDecimal amount = BigDecimal.ZERO;
            BigDecimal amountOriginal = BigDecimal.ZERO;
            for (CgnciClaimSettlementVO.DetailVO detail : reinsurer.getDetails()) {
                if (Objects.nonNull(detail.getSettlementAmount())) {
                    amount = amount.add(detail.getSettlementAmount());
                }
                if (Objects.nonNull(detail.getSettlementAmountOriginal())) {
                    amountOriginal = amountOriginal.add(detail.getSettlementAmountOriginal());
                }
            }
            reinsurer.setTotalSettlementAmount(amount);
            reinsurer.setTotalSettlementAmountOriginal(amountOriginal);
        }
    }


}